use crate::expr::Expr;
use std::collections::{HashMap, HashSet};
use std::ops::{Neg, Range};

pub struct Evaluator {}

impl Evaluator {
    pub fn _eval_para_range(
        expr_x: &Expr,
        expr_y: &Expr,
        expr_z: &Expr,
        t_range: Range<f32>,
        dense: f32,
    ) -> HashMap<usize, f32> {
        let mut res = HashMap::new();
        let mut environemnt = HashMap::new();
        for t_count in 0..=(((t_range.end - t_range.start) / dense).trunc() as usize) {
            let t = t_range.start + t_count as f32 * dense;
            environemnt.insert("t".to_string(), Expr::Cstf(t as f32));
            let _x = Evaluator::eval_expr(expr_x, &environemnt);
            let _y = Evaluator::eval_expr(expr_y, &environemnt);
            let z = Evaluator::eval_expr(expr_z, &environemnt);
            res.insert(t_count, z); // 怎么画 x^2+y^2=2? 假设z的范围是0-5？
        }
        res
    }
    /// eval a square range for a function with 2 parameters, sample from given range
    pub fn eval_range(
        expr: &Expr,
        x_range: Range<f32>,
        y_range: Range<f32>,
        dense: f32,
    ) -> HashMap<(usize, usize), f32> {
        // 按照范围的hash表？
        let mut set = HashSet::new();
        expr.count_vals(&mut set);
        let mut set = set.iter();
        let default_name = ["x".to_string(), "y".to_string()];
        let x_name = set.next().unwrap_or(&default_name[0]);
        let y_name = set.next().unwrap_or(&default_name[1]);
        let mut res = HashMap::new();
        let mut environemnt = HashMap::new();
        for x_count in 0..=(((x_range.end - x_range.start) / dense).trunc() as usize) {
            let x = x_range.start + x_count as f32 * dense;
            environemnt.insert(x_name.clone(), Expr::Cstf(x as f32));
            for y_count in 0..=(((x_range.end - x_range.start) / dense).trunc() as usize) {
                let y = y_range.start + y_count as f32 * dense;
                environemnt.insert(y_name.clone(), Expr::Cstf(y as f32));
                let z = Evaluator::eval_expr(expr, &environemnt);
                res.insert((x_count, y_count), z);
            }
        }
        res
    }

    pub fn eval_expr(expr: &Expr, env: &HashMap<String, Expr>) -> f32 {
        // didn't deal with nil.
        match &*expr {
            Expr::Cstf(x) => *x,
            Expr::Csti(x) => *x as f32,
            Expr::Var(s) => {
                if let Some(s) = env.get(s) {
                    Evaluator::eval_expr(s, env)
                } else {
                    panic!("value of variable `{s}` not found");
                }
            }
            Expr::Add(a, b) => {
                Evaluator::eval_expr(a.as_deref().unwrap(), env)
                    + Evaluator::eval_expr(b.as_deref().unwrap(), env)
            }
            Expr::Sub(a, b) => {
                Evaluator::eval_expr(a.as_deref().unwrap(), env)
                    - Evaluator::eval_expr(b.as_deref().unwrap(), env)
            }
            Expr::Mul(a, b) => {
                Evaluator::eval_expr(a.as_deref().unwrap(), env)
                    * Evaluator::eval_expr(b.as_deref().unwrap(), env)
            }
            Expr::Div(a, b) => {
                Evaluator::eval_expr(a.as_deref().unwrap(), env)
                    / Evaluator::eval_expr(b.as_deref().unwrap(), env)
            }
            Expr::Pow(a, b) => {
                Evaluator::eval_expr(a.as_deref().unwrap_or_else(|| panic!("base none")), env).powf(
                    Evaluator::eval_expr(b.as_deref().unwrap_or_else(|| panic!("power none")), env),
                )
            }
            Expr::Log(a, b) => {
                Evaluator::eval_expr(b.as_deref().unwrap_or_else(|| panic!("log N none")), env).log(
                    Evaluator::eval_expr(
                        a.as_deref().unwrap_or_else(|| panic!("log base none")),
                        env,
                    ),
                )
            }
            Expr::ArcSin(x) => Evaluator::eval_expr(x.as_deref().unwrap(), env).asin(),
            Expr::ArcCos(x) => Evaluator::eval_expr(x.as_deref().unwrap(), env).acos(),
            Expr::ArcTan(x) => Evaluator::eval_expr(x.as_deref().unwrap(), env).atan(),

            Expr::Sin(x) => Evaluator::eval_expr(x.as_deref().unwrap(), env).sin(),
            Expr::Cos(x) => Evaluator::eval_expr(x.as_deref().unwrap(), env).cos(),
            Expr::Tan(x) => Evaluator::eval_expr(x.as_deref().unwrap(), env).tan(),
            Expr::Neg(x) => Evaluator::eval_expr(x.as_deref().unwrap(), env).neg(),
            Expr::Funcall(_name, _paras) => {
                todo!("funcall: 因未实现赋值语句，故搁置。表达力是一样的")
            }
            _ => todo!("Other type Expr didn't interpret as expressions'"),
        }
    }
    // 偏导数
    pub fn derivative(e: Expr, var: &String) -> Expr {
        // const' -> 0
        if let Expr::Cstf(_) | Expr::Csti(_) = e {
            return Expr::Cstf(0.0);
        }
        let mut set = HashSet::new();
        e.count_vals(&mut set);
        if !set.contains(var) {
            return Expr::Cstf(0.0);
        }
        match e {
            // x' -> 1
            Expr::Var(_) => Expr::Csti(1),
            // f(x) + g(x) -> f' + g'; f(x) - g(x) -> f' - g'
            Expr::Add(Some(x), Some(y)) | Expr::Sub(Some(x), Some(y)) => Expr::Add(
                Some(Box::new(Self::derivative(*x, var))),
                Some(Box::new(Self::derivative(*y, var))),
            ),
            Expr::Mul(Some(x), Some(y)) => Expr::Add(
                Some(Box::new(Expr::Mul(
                    Some(x.clone()),
                    Some(Box::new(Self::derivative(*y.clone(), var))),
                ))),
                Some(Box::new(Expr::Mul(
                    Some(Box::new(Self::derivative(*x, var))),
                    Some(y),
                ))),
            ),
            Expr::Div(Some(x), Some(y)) => Expr::Div(
                Some(Box::new(Expr::Sub(
                    Some(Box::new(Expr::Mul(
                        Some(Box::new(Self::derivative(*x.clone(), var))),
                        Some(y.clone()),
                    ))),
                    Some(Box::new(Expr::Mul(
                        Some(x),
                        Some(Box::new(Self::derivative(*y.clone(), var))),
                    ))),
                ))),
                Some(Box::new(Expr::Pow(Some(y), Some(Box::new(Expr::Csti(2)))))),
            ),
            Expr::Neg(Some(x)) => Expr::Neg(Some(Box::new(Self::derivative(*x, var)))),
            Expr::Sin(Some(x)) => Expr::Mul(
                Some(Box::new(Expr::Cos(Some(x.clone())))),
                Some(Box::new(Self::derivative(*x, var))),
            ),
            Expr::Cos(Some(x)) => Expr::Mul(
                Some(Box::new(Expr::Neg(Some(Box::new(Expr::Sin(Some(
                    x.clone(),
                ))))))),
                Some(Box::new(Self::derivative(*x, var))),
            ),

            Expr::Tan(Some(x)) => Self::derivative(
                Expr::Div(
                    Some(Box::new(Expr::Sin(Some(x.clone())))),
                    Some(Box::new(Expr::Cos(Some(x)))),
                ),
                var,
            ),
            // 未考虑多元函数积分
            // (1-x^2)^(-0.5)
            Expr::ArcSin(Some(x)) => Expr::Mul(
                Expr::Div(
                    Expr::Cstf(1.0).wrap(),
                    Expr::Pow(x.clone().wrap(), Expr::Cstf(0.5).wrap()).wrap(),
                )
                .wrap(),
                Self::derivative(*x, var).wrap(),
            ),
            Expr::ArcCos(Some(x)) => {
                Self::derivative(Expr::Neg(Expr::ArcSin(x.wrap()).wrap()), var)
            }
            Expr::ArcTan(Some(x)) => Expr::Mul(
                Expr::Div(
                    Expr::Cstf(1.0).wrap(),
                    Expr::Add(
                        Expr::Cstf(1.0).wrap(),
                        Expr::Pow(x.clone().wrap(), Expr::Cstf(2.0).wrap()).wrap(),
                    )
                    .wrap(),
                )
                .wrap(),
                Self::derivative(*x, var).wrap(),
            ),
            Expr::Sec(Some(x)) => Self::derivative(
                Expr::Div(Expr::Cstf(1.0).wrap(), Expr::Cos(Some(x)).wrap()),
                var,
            ),
            Expr::Csc(Some(x)) => Self::derivative(
                Expr::Div(Expr::Cstf(1.0).wrap(), Expr::Sin(Some(x)).wrap()),
                var,
            ),
            Expr::Cot(Some(x)) => Self::derivative(
                Expr::Div(Expr::Cstf(1.0).wrap(), Expr::Tan(Some(x)).wrap()),
                var,
            ),
            // NOTE differs 如果两个部分都有变量 区分指数与对数函数?
            Expr::Pow(Some(base), Some(pow)) => {
                let mut set = HashSet::new();
                base.count_vals(&mut set);
                if set.contains(var) {
                    return Expr::Mul(
                        pow.clone().wrap(),
                        Expr::Pow(
                            base.clone().wrap(),
                            Expr::Sub(pow.wrap(), Expr::Cstf(1.0).wrap()).wrap(),
                        )
                        .wrap(),
                    );
                } else {
                    return Expr::Mul(
                        Expr::Pow(base.clone().wrap(), pow.wrap()).wrap(),
                        Expr::Log(Expr::Cstf(std::f32::consts::E).wrap(), base.wrap()).wrap(),
                    );
                }
            }
            Expr::Log(Some(base), Some(real)) => {
                let mut set = HashSet::new();
                base.count_vals(&mut set);
                if set.contains(var) {
                    panic!("No related formula");
                }
                return Expr::Div(
                    Expr::Cstf(1.0).wrap(),
                    Expr::Mul(
                        real.wrap(),
                        Expr::Log(Expr::Cstf(std::f32::consts::E).wrap(), base.wrap()).wrap(),
                    )
                    .wrap(),
                );
            }
            _ => todo!("Need more function derivation support"),
        }
    } // 维持脚本语言？
}
